#ifdef _WIN32
#include <winsock2.h>
#else
#include <sys/types.h> 
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h> 
#include <sys/un.h>
#endif
#include "opencv/cxcore.h"
#include <stdio.h>
#include <jpeglib.h>
#include <jerror.h>
#include "jpeg_streamer.h"

#define OUTPUT_BUF_SIZE 2048

typedef struct {
  struct jpeg_destination_mgr pub ;
  int fd;
  JOCTET *buffer;
} my_destination_mgr;

typedef my_destination_mgr *my_dest_ptr;

void jpeg_io_dest(j_compress_ptr cinfo, int fd);


METHODDEF(void)
init_destination (j_compress_ptr cinfo)
{
  my_dest_ptr dest = (my_dest_ptr) cinfo->dest;

  dest->buffer = (JOCTET *)
    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
    OUTPUT_BUF_SIZE * sizeof(JOCTET));

  dest->pub.next_output_byte = dest->buffer;
  dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
}

/*
* flush buffer to filedescriptor
*/
METHODDEF(boolean)
empty_output_buffer (j_compress_ptr cinfo)
{
  my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
  if (send(dest->fd, (char *) dest->buffer, OUTPUT_BUF_SIZE, 0) != OUTPUT_BUF_SIZE) {
    return (FALSE);
    ERREXIT(cinfo, JERR_FILE_WRITE);
  }
  dest->pub.next_output_byte = dest->buffer;
  dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
  return (TRUE);
}

/*
*/
METHODDEF(void)
term_destination(j_compress_ptr cinfo)
{
  my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
  size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;

  if (datacount > 0) {
    if (send(dest->fd, (char *) dest->buffer, datacount, 0) != datacount) {
      return;
      ERREXIT(cinfo, JERR_FILE_WRITE);
    }
  }
}

void jpeg_io_dest (j_compress_ptr cinfo, int fd)
{
  my_dest_ptr dest;
  if (cinfo->dest == NULL) {
    cinfo->dest = (struct jpeg_destination_mgr *)
      (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_PERMANENT,
      sizeof(my_destination_mgr));
  }
  dest = (my_dest_ptr) cinfo->dest;
  dest->pub.init_destination = init_destination;
  dest->pub.empty_output_buffer = empty_output_buffer;
  dest->pub.term_destination = term_destination;
  dest->fd = fd;
}


int write_jpeg_iplImage(IplImage * frame, int fd, int quality)
{
  JSAMPROW row_ptr[1];
  struct jpeg_compress_struct jpeg;
  struct jpeg_error_mgr jerr;
  char *line, *image;
  int y, x, line_width;
  line =(char *) malloc((frame->width * frame->nChannels) * sizeof(char));
  if (!line)
    return 0;
  jpeg.err = jpeg_std_error (&jerr);
  jpeg_create_compress (&jpeg);
  jpeg.image_width = frame->width;
  jpeg.image_height= frame->height;
  jpeg.input_components = frame->nChannels;
  jpeg.in_color_space = JCS_RGB;
  jpeg_set_defaults (&jpeg);
  jpeg_set_quality (&jpeg, quality, TRUE);
  jpeg.dct_method = JDCT_FASTEST;
  jpeg_io_dest(&jpeg, fd);
  jpeg_start_compress (&jpeg, TRUE);
  row_ptr[0] = (JSAMPROW) line;
  line_width = frame->width * frame->nChannels;
  image = (char *) frame->imageData ;
  for (y = 0; y < frame->height; y++) {
    for (x = 0; x < line_width; x+=frame->nChannels) {
      line[x]   = image[x+2];
      line[x+1] = image[x+1];
      line[x+2] = image[x];
    }
    if (!jpeg_write_scanlines (&jpeg, row_ptr, 1)) {
      jpeg_destroy_compress (&jpeg);
      free (line);
      return 0;
    }
    image += line_width;
  }
  jpeg_finish_compress (&jpeg);
  jpeg_destroy_compress (&jpeg);
  free (line);
  return 1;
}


int write_jpegfile_iplImage(IplImage * frame, FILE * fd, int quality)
{
  JSAMPROW row_ptr[1];
  struct jpeg_compress_struct jpeg;
  struct jpeg_error_mgr jerr;
  char *line, *image;
  int y, x, line_width;
  line =(char *) malloc((frame->width * frame->nChannels) * sizeof(char));
  if (!line)
    return 0;
  jpeg.err = jpeg_std_error (&jerr);
  jpeg_create_compress (&jpeg);
  jpeg.image_width = frame->width;
  jpeg.image_height= frame->height;
  jpeg.input_components = frame->nChannels;
  jpeg.in_color_space = JCS_RGB;
  jpeg_set_defaults (&jpeg);
  jpeg_set_quality (&jpeg, quality, TRUE);
  jpeg.dct_method = JDCT_FASTEST;
  jpeg_stdio_dest(&jpeg, fd);
  jpeg_start_compress (&jpeg, TRUE);
  row_ptr[0] = (JSAMPROW) line;
  line_width = frame->width * frame->nChannels;
  image = (char *) frame->imageData ;
  for (y = 0; y < frame->height; y++) {
    for (x = 0; x < line_width; x+=frame->nChannels) {
      line[x]   = image[x+2];
      line[x+1] = image[x+1];
      line[x+2] = image[x];
    }
    if (!jpeg_write_scanlines (&jpeg, row_ptr, 1)) {
      jpeg_destroy_compress (&jpeg);
      free (line);
      return 0;
    }
    image += line_width;
  }
  jpeg_finish_compress (&jpeg);
  jpeg_destroy_compress (&jpeg);
  free (line);
  return 1;
}


int write_jpegmem_iplImage(IplImage * frame, unsigned char **outbuffer, long unsigned int *outlen, int quality)
{
  JSAMPROW row_ptr[1];
  struct jpeg_compress_struct jpeg;
  struct jpeg_error_mgr jerr;
  char *line, *image;
  int y, x, line_width;
  *outbuffer = NULL;
  *outlen = 0;
  line =(char *) malloc((frame->width * frame->nChannels) * sizeof(char));
  if (!line)
    return 0;
  jpeg.err = jpeg_std_error (&jerr);
  jpeg_create_compress (&jpeg);
  jpeg.image_width = frame->width;
  jpeg.image_height= frame->height;
  jpeg.input_components = frame->nChannels;
  jpeg.in_color_space = JCS_RGB;
  jpeg_set_defaults (&jpeg);
  jpeg_set_quality (&jpeg, quality, TRUE);
  jpeg.dct_method = JDCT_FASTEST;
  jpeg_mem_dest(&jpeg, outbuffer, outlen);
  jpeg_start_compress (&jpeg, TRUE);
  row_ptr[0] = (JSAMPROW) line;
  line_width = frame->width * frame->nChannels;
  image = (char *) frame->imageData ;
  for (y = 0; y < frame->height; y++) {
    for (x = 0; x < line_width; x+=frame->nChannels) {
      line[x]   = image[x+2];
      line[x+1] = image[x+1];
      line[x+2] = image[x];
    }
    if (!jpeg_write_scanlines (&jpeg, row_ptr, 1)) {
      jpeg_destroy_compress (&jpeg);
      free (line);
      return 0;
    }
    image += line_width;
  }
  jpeg_finish_compress (&jpeg);
  jpeg_destroy_compress (&jpeg);
  free (line);
  return 1;
}